﻿using System;
using System.Collections.Generic;
using System.Linq;
using TSU.FAMC.ComputerEngineering.Classes.BooleanMaths.Fundamental;
using TSU.FAMC.ComputerEngineering.Classes.BooleanMaths.Functions;
using TSU.FAMC.ComputerEngineering.Classes.Enums;

namespace TSU.FAMC.ComputerEngineering.Classes.Diagnostics
{
    /// <summary>
    /// Encapsulates functions for analyzing a-,b-faults of boolean systems.
    /// </summary>
    public class AbFaultAnalyzer
    {
        // Properties

        /// <summary>
        /// Test for a-,b-faults.
        /// </summary>
        public List<AbTest> AbTest;


        // Methods

        /// <summary>
        /// Default constructor.
        /// </summary>
        public AbFaultAnalyzer()
        {
            AbTest = new List<AbTest>();
        }


        /// <summary>
        /// Returns test for a-,b-faults of the system.
        /// </summary>
        /// <param name="system">Boolean System for analysis.</param>
        /// <returns>List of PDF tests.</returns>
        public void BuildTest(BooleanSystem system)
        {
            AbTest.Clear();

            // iterating through system functions
            foreach (var function in system.Functions)
            {
                // building SOP for current function
                var sopOriginal = function.Domain1.ToList();
                var sop = function.Domain1.Select(cube => new Cube(cube)).ToList();

                // iterating through conjunctions of SOP for current function
                for (var i = 0; i < sop.Count; i++)
                {
                    // ------------------------------------------------------
                    // Step 1. Building a-test for current conjunctions
                    // ------------------------------------------------------

                    var conj = sop[i];

                    // building D for conjunction
                    var d = new CubeSet();
                    for (var j = 0; j < sop.Count; j++)
                    {
                        if (i == j)
                        {
                            continue;
                        }

                        // skipping orthogonal conjunctions
                        if (sop[j].IsOrthogonalTo(conj))
                        {
                            continue;
                        }

                        d.Add(new Cube(sop[j]));
                    }


                    // deleting variables from D identical to conj
                    foreach (var cube in d)
                    {
                        for (var j = 0; j < conj.Dimension; j++)
                        {
                            if (conj[j] != cube[j])
                            {
                                continue;
                            }

                            cube[j] = BooleanValue.Undefined;
                        }
                    }


                    // calculating the root of the equaion D = 0
                    var root = d.EquationNullRoot;

                    if (d.Count > 0 && root.Rank == 0)
                    {
                        // D == 1
                        // Error. a-test must exists
                        throw new Exception("a-test must exists!");
                    }

                    if (root.Dimension == 0)
                    {
                        root = new Cube(conj.Dimension);
                        for (var j = 0; j < root.Dimension; j++)
                        {
                            root[j] = BooleanValue.Undefined;
                        }
                    }


                    // building a-test
                    var aTestVector = new Cube(conj);
                    for (var j = 0; j < aTestVector.Dimension; j++)
                    {
                        if (root[j] == BooleanValue.Undefined)
                        {
                            continue;
                        }

                        aTestVector[j] = root[j];
                    }

                    var aTest = new AbTest {Function = function, Conjunction = conj, Vector = aTestVector};

                    // checking a-test
                    if (function.Calculate(aTestVector) != BooleanValue.True)
                    {
                        throw new Exception("Test vector for a-fault must belongs to the Domain1 of the function.");
                    }

                    // removing current conjunction and checking result
                    var conjOriginal = sopOriginal[i];
                    function.Domain1.Remove(conjOriginal);
                    if (function.Calculate(aTestVector) == BooleanValue.True)
                    {
                        throw new Exception("Result of applying test vector for a-fault of function without testing conjunction must be equal to 0!");
                    }
                    function.Domain1.Add(conjOriginal);


                    // memorizing a-test
                    AbTest.Add(aTest);


                    // ------------------------------------------------------
                    // Step 2. Building b-tests for current conjunctions
                    // ------------------------------------------------------

                    for (var k = 0; k < conj.Dimension; k++)
                    {
                        if (conj[k] == BooleanValue.Undefined)
                        {
                            // skipping undefined bits
                            continue;
                        }

                        // building complement of conj for current bit
                        var conjComplement = new Cube(conj);
                        conjComplement[k] = conj[k] == BooleanValue.True ? BooleanValue.False : BooleanValue.True;

                        // building D for complement
                        var dComplement = new CubeSet();
                        for (var j = 0; j < sop.Count; j++)
                        {
                            if (i == j)
                            {
                                continue;
                            }

                            // skipping orthogonal conjunctions
                            if (sop[j].IsOrthogonalTo(conjComplement))
                            {
                                continue;
                            }

                            dComplement.Add(new Cube(sop[j]));
                        }


                        // deleting variables from DComplement identical to conjComplement
                        foreach (var cube in dComplement)
                        {
                            if (cube == conjComplement)
                            {
                                // skipping current conj
                                continue;
                            }

                            for (var j = 0; j < conjComplement.Dimension; j++)
                            {
                                if (conjComplement[j] != cube[j])
                                {
                                    continue;
                                }

                                cube[j] = BooleanValue.Undefined;
                            }
                        }


                        // calculating the root of the equaion DComplement = 0
                        var rootComplement = dComplement.EquationNullRoot;

                        if (dComplement.Count > 0 && rootComplement.Rank == 0)
                        {
                            // D == 1
                            // Error. b-test must exists
                            throw new Exception("b-test must exists!");
                        }

                        if (rootComplement.Dimension == 0)
                        {
                            rootComplement = new Cube(conjComplement.Dimension);
                            for (var j = 0; j < rootComplement.Dimension; j++)
                            {
                                rootComplement[j] = BooleanValue.Undefined;
                            }
                        }


                        // building b-test
                        var bTestVector = new Cube(conjComplement);
                        for (var j = 0; j < bTestVector.Dimension; j++)
                        {
                            if (rootComplement[j] == BooleanValue.Undefined)
                            {
                                continue;
                            }

                            bTestVector[j] = rootComplement[j];
                        }

                        var bTest = new AbTest
                                        {
                                            Function = function,
                                            Conjunction = conj,
                                            Vector = bTestVector,
                                            Bit = k
                                        };

                        // checking b-test
                        if (function.Calculate(bTestVector) == BooleanValue.True)
                        {
                            throw new Exception("Test vector for b-fault must belongs to the Domain0 of the function.");
                        }

                        // removing current bit and checking result
                        var currentValue = conjOriginal[k];
                        conjOriginal[k] = currentValue == BooleanValue.True ? BooleanValue.False : BooleanValue.True;
                        if (function.Calculate(bTestVector) != BooleanValue.True)
                        {
                            throw new Exception("Result of applying test vector for b-fault of function conjunction without testing bit must be equal to 1!");
                        }
                        conjOriginal[k] = currentValue;


                        // memorizing a-test
                        AbTest.Add(bTest);
                    }
                }
            }
        }
    }
}
